#include <QDebug>
#include <QHeaderView>
#include <QFileInfo>
#include <QDir>
#include <QMenu>

#include "Include/IconProvider.h"
#include "ProjectTreeModel.h"
#include "Include/Project.h"
#include "ProjectProperties.h"
#include "ProjectManager_p.h"
#include "MenuFactory.h"
#include "RenameDlg.h"
#include "AddNewItem.h"
#include "NewProject.h"
#include "ProjectRunner.h"

namespace Plugins
{

ProjectManagerPrivate::ProjectManagerPrivate():
    _project(NULL),
    _editors(NULL)
{

}

void ProjectManagerPrivate::init()
{
    Q_Q(ProjectManager);
    q->header()->hide();
    q->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(q, SIGNAL(activated(QModelIndex)), SLOT(onItemActivated(QModelIndex)));
    connect(q, SIGNAL(collapsed(QModelIndex)), SLOT(onItemCollapsed(QModelIndex)));
    connect(q, SIGNAL(expanded(QModelIndex)), SLOT(onItemExpanded(QModelIndex)));
    connect(q, SIGNAL(customContextMenuRequested(QPoint)), SLOT(onMenuShow(QPoint)));
}

ProjectManagerPrivate::~ProjectManagerPrivate()
{
    delete _project;
}

void ProjectManagerPrivate::loadProject(const QString & path)
{
    Q_Q(ProjectManager);
    if (_project){
        q->closeProject();
    }

    QString projectFile = path;
    QFileInfo info(path);
    if (info.isDir()){
        QDir dir(path);
        foreach(QString item, dir.entryList(QDir::Files)){
            if (item.split(".").last() == "pynotoprj")
                projectFile = projectFile+QDir::separator()+item;
        }
    }
    QFileInfo file(projectFile);
    _project = new Project(projectFile);
    connect(_project, SIGNAL(needReloadTree()), q, SLOT(reloadTree()));
    ProjectTreeModel * model = new ProjectTreeModel(file.absolutePath(), _project, this);
    model->setIconProvider(IconProvider::instance());
    q->setModel(model);
    emit q->projectAvailable(true);
    restoreSession();
}

void ProjectManagerPrivate::closeProject()
{
    Q_Q(ProjectManager);
    delete _project;
    _project = NULL;
    QAbstractItemModel *model = q->model();
    q->setModel(NULL);
    delete model;
}

void ProjectManagerPrivate::onItemActivated(const QModelIndex & index)
{
    Q_Q(ProjectManager);
    QString path = index.data(Qt::EditRole).toString();
    if (QFileInfo(path).isFile()){
        emit q->openFile(path);
        _project->openFile(path);
    }
}

void ProjectManagerPrivate::onItemCollapsed(const QModelIndex & index)
{
    if (_project)
        _project->closeDir(index.data(Qt::EditRole).toString());
}

void ProjectManagerPrivate::onItemExpanded(const QModelIndex & index)
{
    if (_project)
        _project->openDir(index.data(Qt::EditRole).toString());
}

void ProjectManagerPrivate::restoreSession()
{
    Q_Q(ProjectManager);
    q->blockSignals(true);
    foreach(QString dir, _project->openedDirs())
        foreach(QModelIndex index, qobject_cast<ProjectTreeModel *>(q->model())->indexForPath(dir))
            q->expand(index);

    q->blockSignals(false);
    foreach(QString file, _project->openedFiles()){
        emit q->openFile(file);
    }
}

void ProjectManagerPrivate::projectProperties(bool asNew)
{
    if (asNew) {
        NewProject newp;
        if (newp.exec() != QDialog::Accepted)
            return;
        loadProject(newp.path());
    }
    ProjectProperties prop(_project);
    prop.exec();
}

void ProjectManagerPrivate::fileActivated(const QString & path)
{
    Q_Q(ProjectManager);
    foreach(QModelIndex index, qobject_cast<ProjectTreeModel *>(q->model())->indexForPath(path))
        q->setCurrentIndex(index);
}

void ProjectManagerPrivate::onMenuShow(const QPoint & point)
{
    Q_Q(ProjectManager);
    QModelIndex index = q->indexAt(point);
    if (!index.isValid())
        return;

    Node * node = index.data(Qt::UserRole+3).value<Node*>();
    //NodeTypes type = (NodeTypes)index.data(Qt::UserRole+1).toInt();
    //QString spec = index.data(Qt::UserRole+2).toString();
    QMenu *menu = MenuFactory::create(node, q, this);
    menu->popup(q->mapToGlobal(point));
}

void ProjectManagerPrivate::call()
{
    QVariantList call = qobject_cast<QAction*>(sender())->data().value<QVariantList>();
    QMetaObject::invokeMethod(this, call[0].toString().toLatin1(), Qt::QueuedConnection, Q_ARG(Node*, call[1].value<Node*>()));
}

void ProjectManagerPrivate::rename(Node * node)
{
    Q_Q(ProjectManager);
    RenameDlg dlg(node->path(), q);
    dlg.exec();
}

void ProjectManagerPrivate::moduleAdd(Node * node)
{
    Q_Q(ProjectManager);
    AddNewItem dlg(node, q);
    if (dlg.exec() == QDialog::Accepted){
        foreach(QString fileName, dlg.openAfter()){
            emit q->openFile(fileName);
            _project->openFile(fileName);
        }
    }
}

void ProjectManagerPrivate::modifyChanged(const QString & path, bool m)
{
    Q_Q(ProjectManager);
    qobject_cast<ProjectTreeModel*>(q->model())->setModify(path, m);
}

void ProjectManagerPrivate::reloadTree()
{
    Q_Q(ProjectManager);
    qobject_cast<ProjectTreeModel*>(q->model())->onDirectoryChanged("", true);
}

void ProjectManagerPrivate::projectRun(IConsole *console)
{
    Q_Q(ProjectManager);
    ProjectRunner *run = new ProjectRunner(console, _project, q);
    connect(run, SIGNAL(finished()), q, SLOT(runFinished()));
    run->start();
}

QStringList ProjectManagerPrivate::searchInProject(const QString & search, const QString & filePattern, bool isCase, bool isWhole, bool isRegular, const QString & path)
{
    QStringList list;
    QDir dir(path == "" ? _project->path() : path);
    foreach(QFileInfo item, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs)){
        if (item.fileName() == "__pycache__")
            continue;
        list += searchInProject(search, filePattern, isCase, isWhole, isRegular, item.filePath());
    }

    dir.setNameFilters(filePattern.split(";"));
    QString find = search;
    if (isWhole)
        find = "\\b"+search+"\\b";

    QRegExp exp(find, isCase ? Qt::CaseSensitive : Qt::CaseInsensitive);
    if (!isRegular && !isWhole)
        exp.setPatternSyntax(QRegExp::FixedString);

    foreach(QFileInfo item, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)){
        QFile file(item.filePath());
        if (file.open(QIODevice::ReadOnly)) {
            QString line;
            QTextStream in(&file);
            int lin = 1;
            while (!in.atEnd()) {
                line = in.readLine();
                if (line.contains(exp)) {
                    list << QString("%1:%2:%3").arg(item.filePath()).arg(lin).arg(line);
                }
                lin++;
            }
        }
    }

    return list;
}

}
